#include "Sampler.h"

Sampler::Sampler( SurfaceMeshModel *mesh )
	: SurfaceMeshHelper (mesh)
{
	// face area
	computeFaceAreas(FAREA);

	double totalFArea = 0;
	foreach(Face f, mesh->faces())
		totalFArea += farea[f];

	// accumulative face probability based on area
	accumProbability.resize(mesh->n_faces());
	double accum = 0;
	foreach(Face f, mesh->faces())
	{
		accum += farea[f] / totalFArea;
		accumProbability[f.idx()] = accum;
	}

	// Upate normals
	mesh->update_vertex_normals();
	mesh->update_face_normals();
}

std::vector<Sample> Sampler::getSamples(int numSamples)
{
	std::vector<Sample> samples;
	std::vector<Sample> v_samples = getVerticesAsSamples();


	if (numSamples > (int)v_samples.size())
	{
		samples = getRandomBarycentricSamples(numSamples - v_samples.size());
		samples.insert(samples.begin(), v_samples.begin(), v_samples.end());
	}
	else
	{
		QSet<int> subset_ids = subset_indices(numSamples, v_samples.size());

		samples.clear();
		foreach (int id, subset_ids) samples.push_back(v_samples[id]);
	}

	return samples;
}


std::vector<Sample> Sampler::getVerticesAsSamples()
{
	std::vector<Sample> samples;
	vnormal = mesh->get_vertex_property<Vec3d>(VNORMAL);

	foreach (Vertex v, mesh->vertices())
		samples.push_back(Sample(points[v], vnormal[v], v.idx(), true));


	return samples;
}



std::vector<Sample> Sampler::getRandomBarycentricSamples( int numSamples)
{
	fnormal = mesh->get_face_property<Vector3>(FNORMAL); 

	std::vector<Sample> samples;

	double r;
	double b[3];

	for (int i = 0; i < numSamples; i++)
	{
		// r, random point in the area
        r = uniform_double(0, 1);

		// Find corresponding face
		std::vector<double>::iterator it = std::upper_bound(accumProbability.begin(), accumProbability.end(), r);
		uint face_id = it - accumProbability.begin();
		if (face_id == accumProbability.size()) face_id = accumProbability.size() - 1;

		// Add sample from that face
		RandomBarycentric(b);

		Sample sp = BarycentricOnFace(Face(face_id), b[0], b[1]);

		samples.push_back(sp);
	}

	return samples;
}

Sample Sampler::BarycentricOnFace( Face f, double U, double V )
{
	// Get face vertices
	std::vector<Vec3d> v;
	Surface_mesh::Vertex_around_face_circulator fvit, fvend;
	fvit = fvend = mesh->vertices(f);
	do{
		v.push_back(points[fvit]);
	} while (++fvit != fvend);

	// Barycentric
	Vec3d p;
	double b1 = U;
	double b2 = V;
	double b3 = 1.0 - (U + V);


	p.x() = (b1 * v[0].x()) + (b2 * v[1].x()) + (b3 * v[2].x());
	p.y() = (b1 * v[0].y()) + (b2 * v[1].y()) + (b3 * v[2].y());
	p.z() = (b1 * v[0].z()) + (b2 * v[1].z()) + (b3 * v[2].z());

	Vec3d n = fnormal[f];

	return Sample(p, n, f.idx(), false);
}

